Scenario #9640: A Business Api Can Be Used with a Valid Api Key Acting As Global Admin

Any regular business API, here GET /api/hs/office/memberships as an example, can be used with a valid API-key in the Hostsharing-Api-Key header instead of a Keycloak OIDC JWT. The API-key acts as its API_KEY subject with whatever roles got granted to it, here the global ADMIN role, which can view all memberships.

Properties

Given

name value
subjectUuid a91c0004-0000-0000-0000-000000000004
subjectName reporting.key

API_KEY subjects authenticate technical clients via the Hostsharing-Api-Key HTTP header instead of a Keycloak OIDC JWT, e.g. automation programs, completely bypassing Keycloak. Only a global-admin may create API_KEY subjects. The clear-text API-key is returned only once, in the response of creating the API_KEY subject; just its hash gets stored. Like GROUP subjects, API_KEY subjects cannot have an account. Global API_KEY subjects do not belong to a realm, thus their name must neither contain a - (the realm-prefix delimiter) nor a / (the GROUP subject marker).

Create the API_KEY Subject

The response contains the generated clear-text API-key (property apiKey) exactly once; it cannot be retrieved again.

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0004-0000-0000-0000-000000000004",
  "name" : "reporting.key",
  "type" : "API_KEY"
}
EOF
=> status: 201 CREATED a91c0004-0000-0000-0000-000000000004
{
  "uuid" : "a91c0004-0000-0000-0000-000000000004",
  "name" : "reporting.key",
  "organization" : "reporting",
  "type" : "API_KEY",
  "apiKey" : "hsak_reporting.key.50e3acced9cdf5474456bdff9f948cc62108ea1627c92b3c0d13bd0de8d8b614",
  "scopes" : null,
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "2366934b-3ba5-453a-a070-78698db295e0", // globalAdminRoleUuidToGrant
  "object.uuid" : "a8842cb7-7284-468a-9e78-07e6fea8bc98",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#a8842cb7-7284-468a-9e78-07e6fea8bc98:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "2366934b-3ba5-453a-a070-78698db295e0", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0004-0000-0000-0000-000000000004"
}
EOF
=> status: 201 CREATED 2366934b-3ba5-453a-a070-78698db295e0 // globalAdminRoleUuidToGrant

Verify the API-key authenticates as its Subject with the global-admin role, without any JWT

HTTP GET "/api/hs/accounts/current" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
{
  "subject" : {
    "uuid" : "a91c0004-0000-0000-0000-000000000004",
    "name" : "reporting.key",
    "organization" : "reporting",
    "type" : "API_KEY"
  },
  "person" : null,
  "globalAdmin" : true
}

List arbitrary memberships via the business API, authenticated just by the API-key

HTTP GET "/api/hs/office/memberships?partnerNumber=P-10001" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[ {
  "uuid" : "1ebb88e2-0219-4cd3-aa99-b8c7e449e65e",
  "partner" : {
    "uuid" : "8e9e2793-24ea-4d1d-9f56-17e724475ba9",
    "partnerNumber" : "P-10001",
    "partnerRel" : {
      "uuid" : "99cd7791-d436-4490-b935-f7096a957224",
      "anchor" : {
        "uuid" : "882bda1d-4e5e-42f8-afdd-b9741d1c1f4e",
        "personType" : "LEGAL_PERSON",
        "tradeName" : "Hostsharing eG",
        "salutation" : null,
        "title" : null,
        "givenName" : null,
        "familyName" : null
      },
      "holder" : {
        "uuid" : "50df9b54-6221-4116-ad23-49d2876d8100", // Person: First GmbH
        "personType" : "LEGAL_PERSON",
        "tradeName" : "First GmbH",
        "salutation" : null,
        "title" : null,
        "givenName" : null,
        "familyName" : null
      },
      "type" : "PARTNER",
      "mark" : null,
      "contact" : {
        "uuid" : "df451bad-85e7-4d82-95df-3ab8ea3804ab", // Contact: contact-admin@firstcontact.example.com
        "caption" : "first contact",
        "postalAddress" : {
          "country" : "Germany"
        },
        "emailAddresses" : {
          "main" : "contact-admin@firstcontact.example.com"
        },
        "phoneNumbers" : {
          "phone_office" : "+49 123 1234567"
        }
      }
    },
    "details" : {
      "uuid" : "14597f55-b714-44be-ba3c-810b6626d98b",
      "registrationOffice" : "Hamburg",
      "registrationNumber" : "RegNo123456789",
      "birthName" : null,
      "birthPlace" : null,
      "birthday" : null,
      "dateOfDeath" : null
    }
  },
  "mainDebitor" : null,
  "memberNumber" : "M-1000101",
  "memberNumberSuffix" : "01",
  "validFrom" : "2022-10-01",
  "validTo" : "2024-12-30",
  "status" : "CANCELLED",
  "membershipFeeBillable" : true
} ]

generated on 2026-08-10 04:34:35 for branch HEAD